import {
  AfterViewInit,
  Directive,
  ElementRef,
  Host,
  Input,
  OnChanges,
  OnDestroy,
  Optional,
  Renderer2,
  SimpleChanges,
} from '@angular/core';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';
import { TypeUtils } from '../core/utils/type';
import { RowDirective } from './row.directive';
import { LvColSizeType, LvResponsiveSize } from './interface';

@Directive({
  selector: '[lv-col], lv-col, [lvCol]',
  exportAs: 'lvCol',
  host: {
    class: 'lv-col',
  },
})
export class ColDirective implements OnChanges, AfterViewInit, OnDestroy {
  @Input() lvSpan: number;
  @Input() lvXs: LvColSizeType;
  @Input() lvSm: LvColSizeType;
  @Input() lvMd: LvColSizeType;
  @Input() lvLg: LvColSizeType;
  @Input() lvXl: LvColSizeType;
  @Input() lvXXl: LvColSizeType;
  @Input() lvPull: string | number;
  @Input() lvPush: string | number;
  @Input() lvOrder: string | number;
  @Input() lvOffset: string | number;

  private typeUtil = TypeUtils;
  private colPrefixCls = 'lv-col';
  private destroy$ = new Subject<boolean>();
  private classNames: { [key: string]: boolean } = {};

  constructor(
    public elementRef: ElementRef,
    public renderer: Renderer2,
    @Optional() @Host() public rowDirective: RowDirective,
  ) {}

  ngOnChanges(changes: SimpleChanges): void {
    this.setClassNames();
  }

  ngOnDestroy(): void {
    this.destroy$.next(true);
    this.destroy$.complete();
  }

  renderGutter(name: string, gutter: number | null): void {
    const element = this.elementRef.nativeElement;

    if (gutter) {
      this.renderer.setStyle(element, name, `${gutter / 2}px`);
    }
  }

  ngAfterViewInit(): void {
    if (this.rowDirective) {
      this.rowDirective.reallyGutter$
        .pipe(takeUntil(this.destroy$))
        .subscribe(([horizontalGutter, verticalGutter]): void => {
          this.renderGutter('padding-left', horizontalGutter);
          this.renderGutter('padding-right', horizontalGutter);
          this.renderGutter('padding-top', verticalGutter);
          this.renderGutter('padding-bottom', verticalGutter);
        });
    }
  }

  setClassNames(): void {
    const classes = {
      [`${this.colPrefixCls}-${this.lvSpan}`]: this.typeUtil.isNotNil(this.lvSpan),
      [`${this.colPrefixCls}-offset-${this.lvOffset}`]: this.typeUtil.isNotNil(this.lvOffset),
      [`${this.colPrefixCls}-order-${this.lvOrder}`]: this.typeUtil.isNotNil(this.lvOrder),
      [`${this.colPrefixCls}-pull-${this.lvPull}`]: this.typeUtil.isNotNil(this.lvPull),
      [`${this.colPrefixCls}-push-${this.lvPush}`]: this.typeUtil.isNotNil(this.lvPush),
      ...this.getClassNames(),
    };

    let classKeys = Object.keys(this.classNames);

    for (const key of classKeys) {
      this.renderer.removeClass(this.elementRef.nativeElement, key);
    }

    this.classNames = { ...classes };
    classKeys = Object.keys(this.classNames);

    for (const key of classKeys) {
      if (this.classNames[key]) {
        this.renderer.addClass(this.elementRef.nativeElement, key);
      }
    }
  }

  getClassNames(): { [key: string]: any } {
    const listOfSize: Array<keyof ColDirective> = ['lvXs', 'lvSm', 'lvMd', 'lvLg', 'lvXl', 'lvXXl'];
    const listClassName: { [key: string]: any } = {};

    listOfSize.forEach((size): void => {
      const sizeItem = size.replace('lv', '').toLocaleLowerCase();
      if (this.typeUtil.isNotNil(this[size])) {
        if (this.typeUtil.isNumber(this[size]) || this.typeUtil.isString(this[size])) {
          listClassName[`${this.colPrefixCls}-${sizeItem}-${this[size]}`] = true;
        } else {
          const responsiveSize = this[size] as LvResponsiveSize;
          const prefixes: Array<keyof LvResponsiveSize> = ['span', 'offset', 'order', 'pull', 'push'];

          prefixes.forEach((item): void => {
            const prefixClass = item === 'span' ? '-' : `-${item}-`;
            listClassName[`${this.colPrefixCls}-${sizeItem}${prefixClass}${responsiveSize[item]}`] =
              responsiveSize && this.typeUtil.isNotNil(responsiveSize[item]);
          });
        }
      }
    });

    return listClassName;
  }
}
